home *** CD-ROM | disk | FTP | other *** search
/ Software Explosion / Software Explosion (Fore-Matt Home Computing)(1996).iso / games / windows / life / life.c < prev    next >
C/C++ Source or Header  |  1991-05-28  |  35KB  |  963 lines

  1. /****************************************************************************
  2. *
  3. *  FILE:          LIFE.C
  4. *
  5. *  DESCRIPTION:   Windows 3.0 Version of the classic simulation of "LIFE".
  6. *                 This program incorporates a minor twist in the basic
  7. *                 algorithm by utilizing color to represent various
  8. *                 life "phases".  The final phase of any life cycle is
  9. *                 always death!
  10. *
  11. *                 This software is hereby placed in the public domain.
  12. *                 Use and abuse it at your own risk!
  13. *
  14. *
  15. *  AUTHOR:        Tom Wheeler
  16. *                 31294 Morlock
  17. *                 Livonia, Mich.  48152
  18. *                 [72037,1742]
  19. *
  20. *****************************************************************************
  21. *
  22. *     DATE        VER                     DESCRIPTION
  23. *  ----------     ---     ---------------------------------------------------
  24. *   05/20/91      1.0     Initial Development 1.0
  25. *   05/22/91      1.1     Added Color Support
  26. *
  27. ****************************************************************************/
  28.  
  29. #include <windows.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <time.h>
  34. #include "life.h"
  35.  
  36. #define CLASSNAME       "LifeClass"
  37. #define APPNAME         "Life"
  38.  
  39. #define DEAD              0         /* state: cell is dead                 */
  40. #define ALIVE           0x1         /* state: cell is alive                */
  41. #define CHILD           0x2         /* state: cell is a child              */
  42. #define ADOLESCENT      0x4         /* state: cell is an adolescent        */
  43. #define ADULT           0x8         /* state: cell is an adult             */
  44. #define MIDAGED        0x10         /* state: cell is middle aged          */
  45. #define RETIRED        0x20         /* state: cell is retirement age       */
  46. #define SENIOR         0x40         /* state: cell is a senior citizen     */
  47. #define METHUSULA      0x80         /* state: cell is to be terminated     */
  48. #define AGECYCLE         10         /* # of cycles till next age           */
  49.  
  50. #define MAX_XCELL       100         /* max number of horizontal cells      */
  51. #define MAX_YCELL       100         /* max number of vertical cells        */
  52. #define DEFAULT_WIDTH    15         /* default number of cells to display  */
  53. #define DEFAULT_HEIGHT   15         /* default number of cells to display  */
  54. #define CYCLE_TIMER       1
  55.  
  56. /* Macro to get a random integer within a specified range */
  57. #define getrandom( min, max ) ((rand() % (int)(((max)+1) - (min))) + (min))
  58.  
  59. typedef unsigned char UCHAR;
  60.  
  61. /* MATRIX defines the cell matrix used to represent the grid of entities.
  62.  * Life states are represented as the low byte of the matrix entry, the high
  63.  * byte is used to is used to keep a count representing the age of the
  64.  * individual entities.
  65.  */
  66. typedef int MATRIX[MAX_XCELL+2][MAX_YCELL+2];
  67.  
  68. typedef struct _Life {
  69.    int CellX;                       /* individual cell width               */
  70.    int CellY;                       /* individual cell height              */
  71.    int TopCellX;                    /* horiz. id of first cell displayed   */
  72.    int TopCellY;                    /* vert. id of frist cell displayed    */
  73.    int CellsPerPageX;               /* num of visible X cells on screen    */
  74.    int CellsPerPageY;               /* num of visible Y cells on screen    */
  75.    BOOL Grid;                       /* grid on/off state                   */
  76.    BOOL Run;                        /* run state of matrix                 */
  77.    MATRIX Matrix;                   /* state matrix                        */
  78. }LIFE;
  79. typedef LIFE *PLIFE;
  80.  
  81. MATRIX Scratch;                     /* temporary cell matrix               */
  82. HWND hInst = NULL;                    /* instance variable                   */
  83. HMENU hMenuFrame = NULL;               /* instance menu handle                */
  84. HWND hWndFrame = NULL;              /* instance window handle              */
  85. HANDLE hAccelFrame = NULL;          /* instance keyboard accelerator       */
  86. HANDLE hMatrix = NULL;              /* handle to cell matrix structure     */
  87.  
  88. HPEN hBluePen = NULL;               /* Blue drawing pen                    */
  89. HPEN hGreenPen = NULL;              /* Green drawing pen                   */
  90. HPEN hCyanPen = NULL;               /* Cyan drawing pen                    */
  91. HPEN hRedPen = NULL;                /* Red drawing pen                     */
  92. HPEN hMagentaPen = NULL;            /* Magenta drawing pen                 */
  93. HPEN hBrownPen = NULL;              /* Brown drawing pen                   */
  94. HPEN hGrayPen = NULL;               /* Gray drawing pen                    */
  95. HPEN hLightRedPen = NULL;           /* Light Red drawing pen               */
  96. HPEN hWhitePen = NULL;              /* White drawing pen                   */
  97.  
  98. HBRUSH hBlueBrush = NULL;           /* Solid Blue brush                    */
  99. HBRUSH hGreenBrush = NULL;          /* Solid Green brush                   */
  100. HBRUSH hCyanBrush = NULL;           /* Solid Cyan brush                    */
  101. HBRUSH hRedBrush = NULL;            /* Solid Red brush                     */
  102. HBRUSH hMagentaBrush = NULL;        /* Solid Magenta brush                 */
  103. HBRUSH hBrownBrush = NULL;          /* Solid Brown brush                   */
  104. HBRUSH hGrayBrush = NULL;           /* Solid Gray brush                    */
  105. HBRUSH hLightRedBrush = NULL;       /* Solid Light Red brush               */
  106. HBRUSH hWhiteBrush = NULL;          /* Solid White brush                   */
  107.  
  108. PLIFE pLife = NULL;                 /* pointer to cell life structure      */
  109. int iSmall;                          /* size in pixels of a small cell      */
  110. int iNorm;                           /* size in pixels of a normal cell     */
  111. int iLarge;                         /* size in pixels of a large cell      */
  112. int iCycleTime = 250;               /* default timer (milliseconds)        */
  113. WORD wCycleTimer = 0;               /* timer used for life cycling         */
  114.  
  115. /****************************************************************************
  116. *  void DrawGridLines(HDC hDC,RECT *rect)
  117. *
  118. *  Description:   DrawsGridLines on the Life Display Window
  119. *
  120. *  Input:         hDC   - Device Context to draw on
  121. *                 rect  - pointer to client rectangle
  122. *
  123. *  Output:        N/A
  124. ****************************************************************************/
  125. void DrawGridLines(HDC hDC,RECT *rect)
  126. {
  127.    int iX,iY;
  128.  
  129.    SelectObject(hDC,hGrayPen);
  130.    for(iX = pLife->CellX; iX < rect->right; iX += pLife->CellX) {
  131.       MoveTo(hDC,iX,0);
  132.       LineTo(hDC,iX,rect->bottom);
  133.    }
  134.    for(iY = pLife->CellY; iY < rect->bottom; iY += pLife->CellY) {
  135.       MoveTo(hDC,0,iY);
  136.       LineTo(hDC,rect->right,iY);
  137.    }
  138. }
  139.  
  140. /****************************************************************************
  141. *  void DrawCell(HDC hMouseMoveDC,int iXPos,int iYPos,UCHAR ucState)
  142. *
  143. *  Description:   Draws the cell at the indicated grid position in the color
  144. *                 appropriate to its current state.
  145. *
  146. *  Input:         hDC       - Device Context to draw on
  147. *                 iXPos
  148. *                 iYPos    - Screen Matrix position to draw at
  149. *                 ucState  - State to draw
  150. *
  151. *  Output:        N/A
  152. ****************************************************************************/
  153. void DrawCell(HDC hDC,int iXPos,int iYPos,UCHAR ucState)
  154. {
  155.    int iX,iY;
  156.    HPEN hPen;
  157.    HBRUSH hBrush;
  158.  
  159.    iX = iXPos * pLife->CellX;
  160.    iY = iYPos * pLife->CellY;
  161.    if(ucState & ALIVE) {
  162.       if(ucState & SENIOR) {
  163.          hPen = hBluePen;
  164.          hBrush = hBlueBrush;
  165.       }
  166.       else if(ucState & RETIRED) {
  167.          hPen = hBrownPen;
  168.          hBrush = hBrownBrush;
  169.       }
  170.       else if(ucState & MIDAGED) {
  171.          hPen = hMagentaPen;
  172.          hBrush = hMagentaBrush;
  173.       }
  174.       else if(ucState & ADULT) {
  175.          hPen = hRedPen;
  176.          hBrush = hRedBrush;
  177.       }
  178.       else if(ucState & ADOLESCENT) {
  179.          hPen = hCyanPen;
  180.          hBrush = hCyanBrush;
  181.       }
  182.       else if(ucState & CHILD) {
  183.          hPen = hGreenPen;
  184.          hBrush = hGreenBrush;
  185.       }
  186.    }
  187.    else {
  188.       hPen = hWhitePen;
  189.       hBrush = hWhiteBrush;
  190.    }
  191.    SelectObject(hDC,hPen);
  192.    SelectObject(hDC,hBrush);
  193.    Rectangle(hDC,iX+2,iY+2,iX+pLife->CellX-1,iY+pLife->CellY-1);
  194. }
  195.  
  196. /****************************************************************************
  197. *  void DrawMatrix(HDC hDC)
  198. *
  199. *  Description:   Draws The Matrix (only draws the cells that will be visible
  200. *                 in the display window)
  201. *
  202. *  Input:         hDC - Device Context to draw on
  203. *
  204. *  Output:        N/A
  205. ****************************************************************************/
  206. void DrawMatrix(HDC hDC)
  207. {
  208.    int iX,iY,iXPos,iYPos,iCellTemp;
  209.  
  210.    for(iXPos = 0,iX = pLife->TopCellX;
  211.        iX < pLife->CellsPerPageX + pLife->TopCellX; iX++,iXPos++) {
  212.       for(iYPos = 0,iY = pLife->TopCellY;
  213.           iY < pLife->CellsPerPageY + pLife->TopCellY; iY++,iYPos++) {
  214.          iCellTemp = pLife->Matrix[iX][iY] & 0xff;
  215.          if(iCellTemp & ALIVE)
  216.             DrawCell(hDC,iXPos,iYPos,(UCHAR)iCellTemp);
  217.       }
  218.    }
  219. }
  220.  
  221. /****************************************************************************
  222. *  void CycleMatrix(HDC hDC)
  223. *
  224. *  Description:   Runs the Life Algorithm on the Cell Matrix (only updates
  225. *                 the cells that would be visible, the matrix is forced to
  226. *                 wrap at the limits of the screen matrix).
  227. *
  228. *  Input:         hDC - Device Context to draw on
  229. *
  230. *  Output:        N/A
  231. ****************************************************************************/
  232. void CycleMatrix(HDC hDC)
  233. {
  234.    register int iX,iY;
  235.    int iXPos,iYPos,iNeighbor,iCellTemp;
  236.    UCHAR ucState;
  237.    BOOL bNoneAlive = TRUE;
  238.  
  239.    for(iX = pLife->TopCellX,iXPos = 0; iX < (pLife->CellsPerPageX +
  240.        pLife->TopCellX); iX++,iXPos++) {
  241.       for(iY = pLife->TopCellY,iYPos = 0; iY < (pLife->CellsPerPageY +
  242.           pLife->TopCellY); iY++,iYPos++) {
  243.          iNeighbor = 0;
  244.          if(pLife->Matrix[iX-1][iY] & ALIVE)
  245.             iNeighbor++;
  246.          if(pLife->Matrix[iX+1][iY] & ALIVE)
  247.             iNeighbor++;
  248.          if(pLife->Matrix[iX][iY-1] & ALIVE)
  249.             iNeighbor++;
  250.          if(pLife->Matrix[iX][iY+1] & ALIVE)
  251.             iNeighbor++;
  252.          if(pLife->Matrix[iX-1][iY-1] & ALIVE)
  253.             iNeighbor++;
  254.          if(pLife->Matrix[iX+1][iY+1] & ALIVE)
  255.             iNeighbor++;
  256.          if(pLife->Matrix[iX-1][iY+1] & ALIVE)
  257.             iNeighbor++;
  258.          if(pLife->Matrix[iX+1][iY-1] & ALIVE)
  259.             iNeighbor++;
  260.          ucState = (UCHAR)pLife->Matrix[iX][iY];
  261.          if(ucState == DEAD) {   /* birth of a cell if 3 neighbors present */
  262.             if(iNeighbor == 3) {
  263.                Scratch[iX][iY] = ALIVE | CHILD;
  264.                DrawCell(hDC,iXPos,iYPos,ALIVE | CHILD);
  265.             }
  266.             else {
  267.                Scratch[iX][iY] = pLife->Matrix[iX][iY];
  268.             }
  269.          }
  270.          else {
  271.             bNoneAlive = FALSE;
  272.             if((iNeighbor < 2) || (iNeighbor > 3)) { /* kill it off */
  273.                Scratch[iX][iY] = DEAD;
  274.                DrawCell(hDC,iXPos,iYPos,DEAD);
  275.             }
  276.             else {
  277.                /* Keep cell and increment cycle count.  If cycle count limit
  278.                 * is reached, advance cell to next life cycle
  279.                 */
  280.                Scratch[iX][iY] = pLife->Matrix[iX][iY] + 0x100;
  281.                if(!((Scratch[iX][iY] & 0xff00) % (AGECYCLE << 8))) {
  282.                   iCellTemp = ((Scratch[iX][iY] & 0xff) << 1) | ALIVE;
  283.                   if(iCellTemp <= METHUSULA)
  284.                      Scratch[iX][iY] = (Scratch[iX][iY] & 0xff00) + iCellTemp;
  285.                   else {
  286.                      /* if it has aged past SENIOR, kill it off */
  287.                      Scratch[iX][iY] = iCellTemp = DEAD;
  288.                   }
  289.                   DrawCell(hDC,iXPos,iYPos,(UCHAR)iCellTemp);
  290.                }
  291.             }
  292.          }
  293.       }
  294.    }
  295.    /* copy all changes to permanent cell matrix */
  296.    memmove(&pLife->Matrix,&Scratch,sizeof(MATRIX));
  297.    /* randomize the matrix if no alive cells are displayed */
  298.    if(bNoneAlive) {
  299.       for(iX = pLife->TopCellX; iX < (pLife->CellsPerPageX +
  300.           pLife->TopCellX); iX++) {
  301.          for(iY = pLife->TopCellY; iY < (pLife->CellsPerPageY +
  302.              pLife->TopCellY); iY++) {
  303.             if(getrandom(0,1)) {
  304.                pLife->Matrix[iX][iY] = ALIVE | CHILD;
  305.             }
  306.          }
  307.       }
  308.    }
  309. }
  310.  
  311. /****************************************************************************
  312. *  void UpdateScrollBarRange(HWND hWnd)
  313. *
  314. *  Description:   Updates the Scroll Bar Ranges
  315. *
  316. *  Input:         hWnd - Window Handle owning the scroll bars
  317. *
  318. *  Output:        N/A
  319. ****************************************************************************/
  320. void UpdateScrollBarRange(HWND hWnd)
  321. {
  322.    RECT rect;
  323.  
  324.    GetClientRect(hWnd,&rect);
  325.    pLife->CellsPerPageX = (rect.right / pLife->CellX) + 1;
  326.    pLife->CellsPerPageY = (rect.bottom / pLife->CellY) + 1;
  327.    pLife->TopCellX = max(1,min(pLife->TopCellX,MAX_XCELL -
  328.                      pLife->CellsPerPageX));
  329.    pLife->TopCellY = max(1,min(pLife->TopCellY,MAX_YCELL -
  330.                      pLife->CellsPerPageY));
  331.    SetScrollRange(hWnd,SB_HORZ,1,MAX_XCELL - pLife->CellsPerPageX,TRUE);
  332.    SetScrollRange(hWnd,SB_VERT,1,MAX_YCELL - pLife->CellsPerPageY,TRUE);
  333. }
  334.  
  335. /****************************************************************************
  336. *  BOOL InitLife(void)
  337. *
  338. *  Description:   Life Initialization
  339. *
  340. *  Input:         N/A
  341. *
  342. *  Output:        Fails if no memory available
  343. ****************************************************************************/
  344. BOOL InitLife(void)
  345. {
  346.    int iWidth,iHeight,iX,iY;
  347.  
  348.    /* allocate local storage to hold the Life Cell Matrix */
  349.    if((hMatrix = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
  350.                              sizeof(LIFE))) == NULL)
  351.       return FALSE;
  352.    pLife = (PLIFE)LocalLock(hMatrix);  /* lock down the memory permanently */
  353.  
  354.    /* set the default Life cell values */
  355.    iX = GetSystemMetrics(SM_CXSCREEN);
  356.    iY = GetSystemMetrics(SM_CYSCREEN);
  357.    iNorm = iY / 25;
  358.    iSmall = iNorm / 2;
  359.    iLarge = iNorm * 2;
  360.    pLife->CellX = iNorm;
  361.    pLife->CellY = iNorm;
  362.    pLife->Grid = TRUE;
  363.    pLife->Run = FALSE;
  364.    pLife->TopCellX = pLife->TopCellY = 1;
  365.    CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_CHECKED);
  366.    /* initialize all cells to DEAD */
  367.    for(iWidth = 0; iWidth < MAX_XCELL; iWidth++)
  368.       for(iHeight = 0; iHeight < MAX_YCELL; iHeight++)
  369.          pLife->Matrix[iWidth][iHeight] = DEAD;
  370.    iWidth = (pLife->CellX * DEFAULT_WIDTH) +
  371.             (GetSystemMetrics(SM_CXFRAME) * 2) +
  372.             GetSystemMetrics(SM_CXHTHUMB);
  373.    iHeight = (pLife->CellY * DEFAULT_HEIGHT) +
  374.              (GetSystemMetrics(SM_CYFRAME) * 2) +
  375.              GetSystemMetrics(SM_CYMENU) +
  376.              GetSystemMetrics(SM_CYVTHUMB) +
  377.              GetSystemMetrics(SM_CYCAPTION);
  378.    SetWindowPos(hWndFrame,NULL,(iX - iWidth) / 2,(iY - iHeight) / 2,
  379.                 iWidth,iHeight,SWP_NOZORDER);
  380. }
  381.  
  382. /****************************************************************************
  383. *  CenterDialog(HWND hDlg)
  384. *
  385. *  Description:   Function to Center a Dialog Box on the Screen
  386. *
  387. *  Input:         hDlg - Handle to Dialog Box to be centered
  388. *
  389. *  Output:        N/A
  390. *****************************************************************************/
  391. void CenterDialog(HWND hDlg)
  392. {
  393.    RECT rect;
  394.    int iX,iY,iNewX,iNewY;
  395.  
  396.    GetWindowRect(hDlg,(RECT far *)&rect);
  397.    iX = GetSystemMetrics(SM_CXSCREEN);
  398.    iY = GetSystemMetrics(SM_CYSCREEN);
  399.    iNewX = (iX/2) - ((rect.right-rect.left)/2);
  400.    iNewY = (iY/2) - ((rect.bottom-rect.top)/2);
  401.    SetWindowPos(hDlg,NULL,max(iNewX,0),max(iNewY,0),rect.right-rect.left,
  402.                 rect.bottom-rect.top,SWP_NOZORDER);
  403. }
  404.  
  405. /****************************************************************************
  406. *
  407. *  BOOL FAR PASCAL TimerEditDlgProc(HWND hDlg,int iMessage,WORD wParam,
  408. *                                  LONG lParam)
  409. *
  410. *  DESCRIPTION:   Life Cycle Time Edit Dialog Box Proc
  411. *
  412. *  INPUT:         hDlg - Handle to Dialog Box
  413. *                 iMessage - Dialog Box message
  414. *                 wParam
  415. *                 lParam - Standard Windows message parameters
  416. *                 Called externally from WINDOWS - Exported Function
  417. *
  418. *  OUTPUT:        N/A
  419. *
  420. ****************************************************************************/
  421. BOOL FAR PASCAL TimerEditDlgProc(HWND hDlg,int iMessage,WORD wParam,
  422.                                 LONG lParam)
  423. {
  424.    BOOL bTransFlag;
  425.    WORD wVal;
  426.    char szTime[10];
  427.  
  428.    switch (iMessage) {
  429.       case WM_COMMAND:
  430.          switch(wParam) {
  431.             case IDOK:
  432.                wVal = GetDlgItemInt(hDlg,TIMER_EDIT,(BOOL FAR *)&bTransFlag,
  433.                                     FALSE);
  434.                if((!bTransFlag) || (wVal < 100) || (wVal > 999)) {
  435.                   MessageBeep(0);
  436.                   sprintf(szTime,"%d",iCycleTime);
  437.                   SetDlgItemText(hDlg,TIMER_EDIT,(LPSTR)szTime);
  438.                }
  439.                else {
  440.                   iCycleTime = wVal;
  441.                   EndDialog(hDlg,TRUE);
  442.                }
  443.                break;
  444.  
  445.             case IDCANCEL:
  446.                EndDialog(hDlg,FALSE);
  447.                break;
  448.          }
  449.          break;
  450.  
  451.       case WM_INITDIALOG:
  452.          sprintf(szTime,"%d",iCycleTime);
  453.          SetDlgItemText(hDlg,TIMER_EDIT,(LPSTR)szTime);
  454.          CenterDialog(hDlg);
  455.          return(TRUE);
  456.  
  457.       default:
  458.          return(FALSE);
  459.    }
  460.    return(FALSE);
  461. }
  462.  
  463. /****************************************************************************
  464. *  AboutDlgProc(hDlg,iMessage,wParam,lParam)
  465. *
  466. *  Description:   About Dialog Box Procedure
  467. *
  468. *  Input:         hDlg      - Handle to Dialog Box
  469. *                 uMessage - Dialog Box message
  470. *                 wParam
  471. *                 lParam     - Standard Windows message parameters
  472. *                 Called externally from WINDOWS
  473. *
  474. *  Output:        N/A
  475. *****************************************************************************/
  476. BOOL FAR PASCAL AboutDlgProc(HWND hDlg,unsigned iMessage,
  477.                              WORD wParam,LONG lParam)
  478. {
  479.    BOOL Ret;
  480.    char szTimeStamp[128];
  481.  
  482.    Ret = FALSE;
  483.    switch (iMessage)
  484.    {
  485.       case WM_INITDIALOG:
  486.          sprintf(szTimeStamp,"Built: %s %s",__DATE__,__TIME__);
  487.          SetDlgItemText(hDlg,ABOUT_TIMESTAMP,szTimeStamp);
  488.          CenterDialog(hDlg);
  489.          Ret = TRUE;
  490.          break;
  491.  
  492.       case WM_COMMAND:
  493.          EndDialog(hDlg, TRUE);
  494.          Ret = TRUE;
  495.          break;
  496.    }
  497.    return Ret;
  498. }
  499.  
  500. /****************************************************************************
  501. *  WndProc(hWnd,iMessage,wParam,lParam)
  502. *
  503. *  Description:   Main Window Proc
  504. *
  505. *  Input:         hWnd      - Handle to window
  506. *                 iMessage - Window message number
  507. *                 wParam,
  508. *                 lParam     - Standard Windows message parameters
  509. *                 Called externally from WINDOWS
  510. *
  511. *  Output:        Calls DefWindowProc() while active,returns 0L ((LONG) FALSE)
  512. *                 when terminating.
  513. ****************************************************************************/
  514. long FAR PASCAL WndProc(HWND hWnd,unsigned iMessage,WORD wParam,LONG lParam)
  515. {
  516.    FARPROC lpProc;
  517.    long    Ret;
  518.    PAINTSTRUCT ps;
  519.    static RECT rect;
  520.    HDC hDC;
  521.    WORD wXMouse,wYMouse;
  522.    UCHAR ucState;
  523.    static int iXDown,iYDown;
  524.    static BOOL bMouseDown = FALSE,bIconic = FALSE;
  525.    static HDC hMouseMoveDC = NULL;
  526.    int iXCell,iYCell;
  527.  
  528.    Ret = (long)FALSE;
  529.  
  530.    switch(iMessage)
  531.    {
  532.       case WM_COMMAND:
  533.          switch (wParam)
  534.          {
  535.             case MENU_RUN:
  536.                if(pLife->Run) {
  537.                   /* stop the Cycle Timer */
  538.                   if(wCycleTimer) {
  539.                      KillTimer(hWnd,CYCLE_TIMER);
  540.                      wCycleTimer = 0;
  541.                    }
  542.                   ModifyMenu(hMenuFrame,MENU_RUN,MF_BYCOMMAND,MENU_RUN,
  543.                              "St&art\t^R");
  544.                   EnableMenuItem(hMenuFrame,MENU_STEP,MF_ENABLED);
  545.                   pLife->Run = FALSE;
  546.                }
  547.                else {
  548.                   /*start the cycle timer */
  549.                   if((wCycleTimer =
  550.                      SetTimer(hWnd,CYCLE_TIMER,iCycleTime,NULL)) == 0) {
  551.                      MessageBox(hWnd,"No More System Timers!",
  552.                         NULL,MB_OK | MB_ICONEXCLAMATION);
  553.                      break;
  554.                   }
  555.                   ModifyMenu(hMenuFrame,MENU_RUN,MF_BYCOMMAND,MENU_RUN,
  556.                              "St&op\t^R");
  557.                   pLife->Run = TRUE;
  558.                   EnableMenuItem(hMenuFrame,MENU_STEP,MF_GRAYED);
  559.                }
  560.                break;
  561.  
  562.             case MENU_STEP:
  563.                hDC = GetDC(hWnd);
  564.                CycleMatrix(hDC);
  565.                ReleaseDC(hWnd,hDC);
  566.                InvalidateRect(hWnd,NULL,FALSE);
  567.                break;
  568.  
  569.             case MENU_CLEAR:
  570.                /* if running, stop before clearing */
  571.                if(pLife->Run)
  572.                   SendMessage(hWnd,WM_COMMAND,MENU_RUN,0L);
  573.                for(iXCell = 0; iXCell < MAX_XCELL; iXCell++) {
  574.                   for(iYCell = 0; iYCell < MAX_YCELL; iYCell++) {
  575.                      pLife->Matrix[iXCell][iYCell] = DEAD;
  576.                      Scratch[iXCell][iYCell] = DEAD;
  577.                   }
  578.                }
  579.                InvalidateRect(hWnd,NULL,TRUE);
  580.                break;
  581.  
  582.             case MENU_GRID:
  583.                if(pLife->Grid) {
  584.                   ModifyMenu(hMenuFrame,MENU_GRID,MF_BYCOMMAND,MENU_GRID,
  585.                              "&Gridlines On\t^G");
  586.                   pLife->Grid = FALSE;
  587.                }
  588.                else {
  589.                   ModifyMenu(hMenuFrame,MENU_GRID,MF_BYCOMMAND,MENU_GRID,
  590.                              "&Gridlines Off\t^G");
  591.                   pLife->Grid = TRUE;
  592.                }
  593.                InvalidateRect(hWnd,NULL,TRUE);
  594.                break;
  595.  
  596.             case MENU_TIMER:
  597.                lpProc = MakeProcInstance((FARPROC) TimerEditDlgProc,hInst);
  598.                DialogBox(hInst,"TIMERDLGBOX",hWnd,lpProc);
  599.                FreeProcInstance(lpProc);
  600.                /* if already running, update the timer */
  601.                if(wCycleTimer) {
  602.                   KillTimer(hWnd,CYCLE_TIMER);
  603.                    if((wCycleTimer =
  604.                      SetTimer(hWnd,CYCLE_TIMER,iCycleTime,NULL)) == 0) {
  605.                      MessageBox(hWnd,"No More System Timers!",
  606.                         NULL,MB_OK | MB_ICONEXCLAMATION);
  607.                      break;
  608.                   }
  609.                }
  610.                break;
  611.  
  612.             case MENU_SMALL:
  613.                CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_CHECKED);
  614.                CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_UNCHECKED);
  615.                CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_UNCHECKED);
  616.                pLife->CellX = iSmall;
  617.                pLife->CellY = iSmall;
  618.                UpdateScrollBarRange(hWnd);
  619.                InvalidateRect(hWnd,NULL,TRUE);
  620.                break;
  621.  
  622.             case MENU_NORM:
  623.                CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_UNCHECKED);
  624.                CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_CHECKED);
  625.                CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_UNCHECKED);
  626.                pLife->CellX = iNorm;
  627.                pLife->CellY = iNorm;
  628.                UpdateScrollBarRange(hWnd);
  629.                InvalidateRect(hWnd,NULL,TRUE);
  630.                break;
  631.  
  632.             case MENU_LARGE:
  633.                CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_UNCHECKED);
  634.                CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_UNCHECKED);
  635.                CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_CHECKED);
  636.                pLife->CellX = iLarge;
  637.                pLife->CellY = iLarge;
  638.                UpdateScrollBarRange(hWnd);
  639.                InvalidateRect(hWnd,NULL,TRUE);
  640.                break;
  641.  
  642.             case MENU_ABOUT:
  643.                lpProc = MakeProcInstance((FARPROC) AboutDlgProc,hInst);
  644.                DialogBox(hInst,"ABOUTBOX",hWnd,lpProc);
  645.                FreeProcInstance(lpProc);
  646.                break;
  647.          }
  648.          break;
  649.  
  650.       case WM_TIMER:
  651.          hDC = GetDC(hWnd);
  652.          CycleMatrix(hDC);
  653.          ReleaseDC(hWnd,hDC);
  654.          if(!bIconic)
  655.             InvalidateRect(hWnd,NULL,FALSE);
  656.          break;
  657.  
  658.       case WM_LBUTTONDOWN:
  659.          iXDown = -1;
  660.          iYDown = -1;
  661.          bMouseDown = TRUE;
  662.          hMouseMoveDC = GetDC(hWnd);
  663.          GetClientRect(hWnd,&rect);
  664.          SelectObject(hMouseMoveDC,GetStockObject(WHITE_PEN));
  665.          break;
  666.  
  667.       case WM_LBUTTONUP:
  668.          /* force draw at current position */
  669.          SendMessage(hWnd,WM_MOUSEMOVE,wParam,lParam);
  670.          if(bMouseDown) {
  671.             bMouseDown = FALSE;
  672.             SelectObject(hMouseMoveDC,GetStockObject(BLACK_PEN));
  673.             ReleaseDC(hWnd,hMouseMoveDC);
  674.          }
  675.          break;
  676.  
  677.       case WM_MOUSEMOVE:
  678.          if(bMouseDown) {
  679.             wXMouse = LOWORD(lParam);
  680.             wYMouse = HIWORD(lParam);
  681.             /* translate mouse position into cell coordinates */
  682.             iXCell = (int)wXMouse / pLife->CellX;
  683.             iYCell = (int)wYMouse / pLife->CellY;
  684.             if((iXCell != iXDown) || (iYCell != iYDown)) {
  685.                iXDown = iXCell;
  686.                iYDown = iYCell;
  687.                ucState = (UCHAR)pLife->Matrix[iXCell+pLife->TopCellX]
  688.                                              [iYCell+pLife->TopCellY];
  689.                ucState = (UCHAR)((ucState & ALIVE) ? DEAD : ALIVE | CHILD);
  690.                pLife->Matrix[iXCell+pLife->TopCellX]
  691.                             [iYCell+pLife->TopCellY] = ucState;
  692.                DrawCell(hMouseMoveDC,iXCell,iYCell,ucState);
  693.             }
  694.          }
  695.          break;
  696.  
  697.       case WM_NCMOUSEMOVE:
  698.          /* look to see if the mouse is outside the client area, if so and
  699.           * the mouse is being captured then release it
  700.           */
  701.          if(bMouseDown) {
  702.             bMouseDown = FALSE;
  703.             SelectObject(hMouseMoveDC,GetStockObject(BLACK_PEN));
  704.             ReleaseDC(hWnd,hMouseMoveDC);
  705.          }
  706.          break;
  707.  
  708.       case WM_VSCROLL:
  709.          switch(wParam) {
  710.             case SB_LINEUP:
  711.                pLife->TopCellY -= 1;
  712.                break;
  713.  
  714.             case SB_PAGEUP:
  715.                pLife->TopCellY -= pLife->CellsPerPageY;
  716.                break;
  717.  
  718.             case SB_LINEDOWN:
  719.                pLife->TopCellY += 1;
  720.                break;
  721.  
  722.             case SB_PAGEDOWN:
  723.                pLife->TopCellY += pLife->CellsPerPageY;
  724.                break;
  725.  
  726.             case SB_THUMBPOSITION:
  727.                pLife->TopCellY = LOWORD(lParam);
  728.                break;
  729.  
  730.             default:
  731.                break;
  732.          }
  733.          pLife->TopCellY = max(1,min(pLife->TopCellY,MAX_YCELL -
  734.                            pLife->CellsPerPageY));
  735.          if(pLife->TopCellY != GetScrollPos(hWnd,SB_VERT)) {
  736.              iXDown = -1;
  737.              iYDown = -1;
  738.             SetScrollPos(hWnd,SB_VERT,pLife->TopCellY,TRUE);
  739.             InvalidateRect(hWnd,NULL,TRUE);
  740.          }
  741.          break;
  742.  
  743.       case WM_HSCROLL:
  744.          switch(wParam) {
  745.             case SB_LINEUP:
  746.                pLife->TopCellX -= 1;
  747.                break;
  748.  
  749.             case SB_PAGEUP:
  750.                pLife->TopCellX -= pLife->CellsPerPageX;
  751.                break;
  752.  
  753.             case SB_LINEDOWN:
  754.                pLife->TopCellX += 1;
  755.                break;
  756.  
  757.             case SB_PAGEDOWN:
  758.                pLife->TopCellX += pLife->CellsPerPageX;
  759.                break;
  760.  
  761.             case SB_THUMBPOSITION:
  762.                pLife->TopCellX = LOWORD(lParam);
  763.                break;
  764.  
  765.             default:
  766.                break;
  767.          }
  768.          pLife->TopCellX = max(1,min(pLife->TopCellX,MAX_XCELL -
  769.                            pLife->CellsPerPageX));
  770.          if(pLife->TopCellX != GetScrollPos(hWnd,SB_HORZ)) {
  771.              iXDown = -1;
  772.              iYDown = -1;
  773.             SetScrollPos(hWnd,SB_HORZ,pLife->TopCellX,TRUE);
  774.             InvalidateRect(hWnd,NULL,TRUE);
  775.          }
  776.          break;
  777.  
  778.       case WM_KEYDOWN:
  779.          switch(wParam) {
  780.             case VK_PRIOR:
  781.                SendMessage(hWnd,WM_VSCROLL,SB_PAGEUP,0L);
  782.                break;
  783.  
  784.             case VK_NEXT:
  785.                SendMessage(hWnd,WM_VSCROLL,SB_PAGEDOWN,0L);
  786.                break;
  787.  
  788.             case VK_UP:
  789.                SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0L);
  790.                break;
  791.  
  792.             case VK_DOWN:
  793.                SendMessage(hWnd,WM_VSCROLL,SB_LINEDOWN,0L);
  794.                break;
  795.  
  796.             case VK_LEFT:
  797.                SendMessage(hWnd,WM_HSCROLL,SB_PAGEUP,0L);
  798.                break;
  799.  
  800.             case VK_RIGHT:
  801.                SendMessage(hWnd,WM_HSCROLL,SB_PAGEDOWN,0L);
  802.                break;
  803.          }
  804.          break;
  805.  
  806.       case WM_SIZE:
  807.          UpdateScrollBarRange(hWnd);
  808.          break;
  809.  
  810.       case WM_PAINT:
  811.          hDC = BeginPaint(hWnd,&ps);
  812.          if(pLife->Grid)
  813.             DrawGridLines(hDC,&ps.rcPaint);
  814.          DrawMatrix(hDC);
  815.          EndPaint(hWnd,&ps);
  816.          break;
  817.  
  818.       case WM_SYSCOMMAND:
  819.          switch(wParam) {
  820.             case SC_MINIMIZE:
  821.                bIconic = TRUE;
  822.                break;
  823.  
  824.             case SC_MAXIMIZE:
  825.             case SC_RESTORE:
  826.                bIconic = FALSE;
  827.                break;
  828.          }
  829.          return DefWindowProc(hWnd, iMessage, wParam, lParam);
  830.          break;
  831.  
  832.       case WM_CREATE:
  833.          hBluePen = CreatePen(PS_SOLID,1,RGB(0,0,128));
  834.          hGreenPen = CreatePen(PS_SOLID,1,RGB(0,128,0));
  835.          hCyanPen = CreatePen(PS_SOLID,1,RGB(0,128,128));
  836.          hRedPen = CreatePen(PS_SOLID,1,RGB(128,0,0));
  837.          hMagentaPen = CreatePen(PS_SOLID,1,RGB(128,0,128));
  838.          hBrownPen = CreatePen(PS_SOLID,1,RGB(128,128,0));
  839.          hGrayPen = CreatePen(PS_SOLID,1,RGB(192,192,192));
  840.          hLightRedPen = CreatePen(PS_SOLID,1,RGB(255,0,0));
  841.          hWhitePen = CreatePen(PS_SOLID,1,RGB(255,255,255));
  842.          hBlueBrush = CreateSolidBrush(RGB(0,0,128));
  843.          hGreenBrush = CreateSolidBrush(RGB(0,128,0));
  844.          hCyanBrush = CreateSolidBrush(RGB(0,128,128));
  845.          hRedBrush = CreateSolidBrush(RGB(128,0,0));
  846.          hMagentaBrush = CreateSolidBrush(RGB(128,0,128));
  847.          hBrownBrush = CreateSolidBrush(RGB(128,128,0));
  848.          hGrayBrush = CreateSolidBrush(RGB(192,192,192));
  849.          hLightRedBrush = CreateSolidBrush(RGB(255,0,0));
  850.          hWhiteBrush = CreateSolidBrush(RGB(255,255,255));
  851.           srand((unsigned)time(NULL));
  852.          break;
  853.  
  854.       case WM_CLOSE:
  855.          if(wCycleTimer) {
  856.             KillTimer(hWnd,CYCLE_TIMER);
  857.             wCycleTimer = 0;
  858.          }
  859.          if(hMatrix != NULL) {
  860.             LocalUnlock(hMatrix);
  861.             LocalFree(hMatrix);
  862.          }
  863.          DeleteObject(hBluePen);
  864.          DeleteObject(hGreenPen);
  865.          DeleteObject(hCyanPen);
  866.          DeleteObject(hRedPen);
  867.          DeleteObject(hMagentaPen);
  868.          DeleteObject(hBrownPen);
  869.          DeleteObject(hGrayPen);
  870.          DeleteObject(hWhitePen);
  871.          DeleteObject(hBlueBrush);
  872.          DeleteObject(hGreenBrush);
  873.          DeleteObject(hCyanBrush);
  874.          DeleteObject(hRedBrush);
  875.          DeleteObject(hMagentaBrush);
  876.          DeleteObject(hBrownBrush);
  877.          DeleteObject(hGrayBrush);
  878.          DeleteObject(hWhiteBrush);
  879.          PostQuitMessage(0);
  880.          break;
  881.  
  882.       default:
  883.          return DefWindowProc(hWnd, iMessage, wParam, lParam);
  884.    }
  885.    return 0L;
  886. }
  887.  
  888. /****************************************************************************
  889. *  int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,
  890. *                     LPSTR lpszCmdLine,int nCmdShow)
  891. *
  892. *  Description:   Life Main Window Function
  893. *
  894. *  Input:         hInstance       - Window instance handle
  895. *                 hPrevInstance     - Previous Instance Handle
  896. *                 lpszCmdLine     - Window Activation Parameters (none req.)
  897. *                 nCmdShow        - Show Window or Icon command
  898. *                 Called externally from WINDOWS
  899. *
  900. *  Output:        Handles Windows message loop,terminates to Windows
  901. *****************************************************************************/
  902. int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,LPSTR lpszCmdLine,
  903.                    int nCmdShow)
  904. {
  905.    HWND     hWnd;
  906.    MSG      msg;
  907.    WNDCLASS wc;
  908.  
  909.    hInst = hInstance;
  910.    if(!hPrevInstance)
  911.    {
  912.       wc.style          = CS_HREDRAW | CS_VREDRAW;
  913.       wc.lpfnWndProc    = WndProc;
  914.       wc.cbClsExtra     = 0;
  915.       wc.cbWndExtra     = 0;
  916.       wc.hInstance      = hInstance;
  917.       wc.hIcon          = LoadIcon(hInstance,"LIFEICON");
  918.       wc.hCursor        = LoadCursor(hInstance,"HAND");
  919.       wc.hbrBackground  = GetStockObject(WHITE_BRUSH);
  920.       wc.lpszMenuName   = "MainMenu";
  921.       wc.lpszClassName  = CLASSNAME;
  922.  
  923.       if(!RegisterClass(&wc)) {
  924.          MessageBox(NULL, "Initialization Error!", APPNAME,
  925.                     MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
  926.          return TRUE;
  927.       }
  928.    }
  929.    hWnd = CreateWindow(CLASSNAME,             /* Window class name          */
  930.                        APPNAME,               /* window caption             */
  931.                        WS_OVERLAPPEDWINDOW | WS_VSCROLL |
  932.                        WS_HSCROLL,            /* window style               */
  933.                        CW_USEDEFAULT,         /* initial x (horiz) position */
  934.                        CW_USEDEFAULT,         /* initial y (vert) position  */
  935.                        CW_USEDEFAULT,         /* initial x size             */
  936.                        CW_USEDEFAULT,         /* initial y size             */
  937.                        NULL,                  /* parent window handle       */
  938.                        NULL,                  /* window menu handle         */
  939.                        hInstance,             /* program instance handle    */
  940.                        NULL);                 /* create parameters          */
  941.  
  942.  
  943.    /* initialize variables */
  944.    hWndFrame = hWnd;                          /* global parent window       */
  945.    hMenuFrame = GetMenu(hWnd);                /* global menu handle         */
  946.    hAccelFrame = LoadAccelerators(hInstance,"LifeKeys"); /* key accelerator */
  947.  
  948.    /* initialize the Life cell structure */
  949.    if(!InitLife())
  950.       return TRUE;
  951.  
  952.    ShowWindow(hWnd,nCmdShow);                 /* shows the window           */
  953.  
  954.    /* message loop */
  955.    while(GetMessage(&msg,NULL,0,0)) {
  956.       if(!TranslateAccelerator(hWnd,hAccelFrame,&msg)) {
  957.           TranslateMessage(&msg);
  958.          DispatchMessage(&msg);
  959.       }
  960.    }
  961.    return msg.wParam;
  962. }
  963.